package com.jim.healio.graphrag.service;

import com.jim.healio.graphrag.model.MedicalEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.stream.Collectors;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 实体验证服务
 * 提供实体存在性检查、模糊匹配和智能建议功能
 */
@Service
public class EntityValidationService {
    
    private static final Logger logger = LoggerFactory.getLogger(EntityValidationService.class);
    
    @Autowired
    private Neo4jGraphService neo4jService;
    
    // 实体缓存，提高查询性能
    private final Map<String, Set<String>> entityCache = new ConcurrentHashMap<>();
    private final Map<String, Set<String>> aliasCache = new ConcurrentHashMap<>();
    
    /**
     * 验证实体是否存在于图谱中
     */
    public boolean validateEntity(String entityName) {
        if (entityName == null || entityName.trim().isEmpty()) {
            return false;
        }
        
        String normalizedName = normalizeEntityName(entityName);
        
        // 1. 检查缓存
        if (isInCache(normalizedName)) {
            return true;
        }
        
        // 2. 在图谱中查询
        List<MedicalEntity> entities = neo4jService.queryEntities(normalizedName);
        
        // 3. 更新缓存
        updateCache(entities);
        
        // 4. 检查是否有精确匹配或别名匹配
        return entities.stream().anyMatch(entity -> 
            matchesEntity(normalizedName, entity)
        );
    }
    
    /**
     * 批量验证实体
     */
    public Map<String, Boolean> validateEntities(List<String> entityNames) {
        logger.info("批量验证 {} 个实体", entityNames.size());
        
        return entityNames.stream()
            .collect(Collectors.toMap(
                name -> name,
                this::validateEntity
            ));
    }
    
    /**
     * 查找相似实体（模糊匹配）
     */
    public List<EntitySuggestion> findSimilarEntities(String entityName, int limit) {
        logger.info("查找相似实体: {}", entityName);
        
        String normalizedName = normalizeEntityName(entityName);
        List<EntitySuggestion> suggestions = new ArrayList<>();
        
        // 1. 获取所有相关实体
        List<MedicalEntity> allEntities = neo4jService.queryEntities(normalizedName);
        
        // 2. 计算相似度并排序
        for (MedicalEntity entity : allEntities) {
            double similarity = calculateSimilarity(normalizedName, entity);
            if (similarity > 0.3) { // 相似度阈值
                EntitySuggestion suggestion = new EntitySuggestion(
                    entity.getId(),
                    entity.getName(),
                    entity.getType(),
                    similarity,
                    entity.getDescription()
                );
                suggestions.add(suggestion);
            }
        }
        
        // 3. 按相似度排序并限制数量
        suggestions.sort((a, b) -> Double.compare(b.getSimilarity(), a.getSimilarity()));
        
        List<EntitySuggestion> limitedSuggestions = suggestions.stream()
            .limit(limit)
            .collect(Collectors.toList());
        
        logger.info("找到 {} 个相似实体建议", limitedSuggestions.size());
        return limitedSuggestions;
    }
    
    /**
     * 智能实体解析：尝试将用户输入解析为有效实体
     */
    public EntityResolutionResult resolveEntity(String userInput) {
        logger.info("智能解析实体: {}", userInput);
        
        String normalizedInput = normalizeEntityName(userInput);
        
        // 1. 精确匹配
        if (validateEntity(normalizedInput)) {
            List<MedicalEntity> exactMatches = neo4jService.queryEntities(normalizedInput);
            MedicalEntity bestMatch = findBestMatch(normalizedInput, exactMatches);
            if (bestMatch != null) {
                return new EntityResolutionResult(
                    true,
                    bestMatch.getId(),
                    bestMatch.getName(),
                    1.0,
                    "精确匹配",
                    new ArrayList<>()
                );
            }
        }
        
        // 2. 模糊匹配
        List<EntitySuggestion> suggestions = findSimilarEntities(normalizedInput, 5);
        if (!suggestions.isEmpty()) {
            EntitySuggestion bestSuggestion = suggestions.get(0);
            
            // 如果最佳建议相似度很高，直接使用
            if (bestSuggestion.getSimilarity() > 0.8) {
                return new EntityResolutionResult(
                    true,
                    bestSuggestion.getEntityId(),
                    bestSuggestion.getEntityName(),
                    bestSuggestion.getSimilarity(),
                    "高相似度匹配",
                    suggestions.subList(1, Math.min(suggestions.size(), 4))
                );
            } else {
                // 否则返回建议列表让用户选择
                return new EntityResolutionResult(
                    false,
                    null,
                    null,
                    0.0,
                    "未找到精确匹配，提供相似建议",
                    suggestions
                );
            }
        }
        
        // 3. 未找到任何匹配
        return new EntityResolutionResult(
            false,
            null,
            null,
            0.0,
            "未找到相关实体",
            new ArrayList<>()
        );
    }
    
    /**
     * 获取实体类型的有效实体列表
     */
    public List<String> getValidEntitiesByType(MedicalEntity.EntityType entityType, int limit) {
        logger.info("获取类型为 {} 的有效实体，限制: {}", entityType, limit);
        
        // 这里需要根据类型查询，暂时使用通用查询
        List<MedicalEntity> entities = neo4jService.queryEntities(entityType.getDescription());
        
        return entities.stream()
            .filter(entity -> entity.getType() == entityType)
            .map(MedicalEntity::getName)
            .limit(limit)
            .collect(Collectors.toList());
    }
    
    /**
     * 获取推荐的实体列表（用于提示LLM）
     */
    public String getEntityHints(MedicalEntity.EntityType entityType) {
        List<String> validEntities = getValidEntitiesByType(entityType, 10);
        
        if (validEntities.isEmpty()) {
            return "暂无可用的" + entityType.getDescription() + "实体";
        }
        
        return "可用的" + entityType.getDescription() + "包括：" + 
               String.join("、", validEntities) + "等";
    }
    
    /**
     * 标准化实体名称
     */
    private String normalizeEntityName(String entityName) {
        return entityName.toLowerCase()
            .trim()
            .replaceAll("\\s+", " ")
            .replaceAll("[,，.。;；:：]", "");
    }
    
    /**
     * 检查实体是否在缓存中
     */
    private boolean isInCache(String normalizedName) {
        for (Set<String> names : entityCache.values()) {
            if (names.contains(normalizedName)) {
                return true;
            }
        }
        
        for (Set<String> aliases : aliasCache.values()) {
            if (aliases.contains(normalizedName)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 更新实体缓存
     */
    private void updateCache(List<MedicalEntity> entities) {
        for (MedicalEntity entity : entities) {
            // 缓存实体名称
            entityCache.computeIfAbsent(entity.getType().name(), k -> new HashSet<>())
                .add(normalizeEntityName(entity.getName()));
            
            // 缓存别名
            Set<String> normalizedAliases = entity.getAliases().stream()
                .map(this::normalizeEntityName)
                .collect(Collectors.toSet());
            aliasCache.computeIfAbsent(entity.getType().name(), k -> new HashSet<>())
                .addAll(normalizedAliases);
        }
    }
    
    /**
     * 检查名称是否匹配实体
     */
    private boolean matchesEntity(String normalizedName, MedicalEntity entity) {
        // 检查实体名称
        if (normalizeEntityName(entity.getName()).equals(normalizedName)) {
            return true;
        }
        
        // 检查别名
        return entity.getAliases().stream()
            .anyMatch(alias -> normalizeEntityName(alias).equals(normalizedName));
    }
    
    /**
     * 计算字符串相似度
     */
    private double calculateSimilarity(String normalizedName, MedicalEntity entity) {
        double maxSimilarity = 0.0;
        
        // 与实体名称的相似度
        maxSimilarity = Math.max(maxSimilarity, 
            stringSimilarity(normalizedName, normalizeEntityName(entity.getName())));
        
        // 与别名的相似度
        for (String alias : entity.getAliases()) {
            maxSimilarity = Math.max(maxSimilarity,
                stringSimilarity(normalizedName, normalizeEntityName(alias)));
        }
        
        return maxSimilarity;
    }
    
    /**
     * 计算两个字符串的相似度（Jaccard相似度）
     */
    private double stringSimilarity(String str1, String str2) {
        if (str1.equals(str2)) {
            return 1.0;
        }
        
        Set<String> words1 = Set.of(str1.split("\\s+"));
        Set<String> words2 = Set.of(str2.split("\\s+"));
        
        Set<String> intersection = new HashSet<>(words1);
        intersection.retainAll(words2);
        
        Set<String> union = new HashSet<>(words1);
        union.addAll(words2);
        
        return union.isEmpty() ? 0.0 : (double) intersection.size() / union.size();
    }
    
    /**
     * 在匹配的实体中找到最佳匹配
     */
    private MedicalEntity findBestMatch(String normalizedName, List<MedicalEntity> entities) {
        return entities.stream()
            .filter(entity -> matchesEntity(normalizedName, entity))
            .findFirst()
            .orElse(null);
    }
    
    /**
     * 实体建议结果类
     */
    public static class EntitySuggestion {
        private final String entityId;
        private final String entityName;
        private final MedicalEntity.EntityType entityType;
        private final double similarity;
        private final String description;
        
        public EntitySuggestion(String entityId, String entityName, 
                               MedicalEntity.EntityType entityType, 
                               double similarity, String description) {
            this.entityId = entityId;
            this.entityName = entityName;
            this.entityType = entityType;
            this.similarity = similarity;
            this.description = description;
        }
        
        // Getters
        public String getEntityId() { return entityId; }
        public String getEntityName() { return entityName; }
        public MedicalEntity.EntityType getEntityType() { return entityType; }
        public double getSimilarity() { return similarity; }
        public String getDescription() { return description; }
        
        @Override
        public String toString() {
            return String.format("%s (%s, %.2f)", entityName, entityType.getDescription(), similarity);
        }
    }
    
    /**
     * 实体解析结果类
     */
    public static class EntityResolutionResult {
        private final boolean resolved;
        private final String entityId;
        private final String entityName;
        private final double confidence;
        private final String reason;
        private final List<EntitySuggestion> suggestions;
        
        public EntityResolutionResult(boolean resolved, String entityId, String entityName,
                                    double confidence, String reason, List<EntitySuggestion> suggestions) {
            this.resolved = resolved;
            this.entityId = entityId;
            this.entityName = entityName;
            this.confidence = confidence;
            this.reason = reason;
            this.suggestions = suggestions;
        }
        
        // Getters
        public boolean isResolved() { return resolved; }
        public String getEntityId() { return entityId; }
        public String getEntityName() { return entityName; }
        public double getConfidence() { return confidence; }
        public String getReason() { return reason; }
        public List<EntitySuggestion> getSuggestions() { return suggestions; }
        
        @Override
        public String toString() {
            if (resolved) {
                return String.format("解析成功: %s (置信度: %.2f)", entityName, confidence);
            } else {
                return String.format("解析失败: %s (建议数: %d)", reason, suggestions.size());
            }
        }
    }
} 